const std = @import("std");
const debug = std.debug;
const mem = std.mem;
const builtin = std.builtin;
const meta = std.meta;
const fmt = std.fmt;
const fs = @import("root").fs;
const math = std.math;

const c = @cImport({
    @cInclude("stdlib.h");
    @cInclude("unistd.h");
});

// 以16进制的方式打印选项, 并 以16进制的方式解析输入
pub fn get_ui_enum(T: type, input: std.fs.File.Reader) T {
    const tag_type: type = std.meta.Tag(T);
    var gpa = std.heap.GeneralPurposeAllocator(.{}){}; // <><>
    defer debug.assert(gpa.deinit() == .ok);
    const allocator: mem.Allocator = gpa.allocator();

    return a: while (true) : (debug.print("输入错误\n", .{})) {
        debug.print("请选择:\n", .{});
        const fields: []const builtin.Type.EnumField = meta.fields(T);
        inline for (fields) |field| {
            debug.print("\t{x}:\t{s}\n", .{ field.value, field.name });
        } else {
            debug.print(">", .{});
        }
        // +1 because '\n', but '\n' is not in slice
        const str: []const u8 = blk: {
            const bits: usize = @typeInfo(tag_type).Int.bits / 8;
            const blk = input.readUntilDelimiterAlloc(allocator, '\n', @max(bits, 1) + @as(usize, 1)); // <><>
            errdefer allocator.free(blk);
            ui_clear();
            break :blk blk catch continue :a;
        };
        defer allocator.free(str); // <><> deinit
        const int: tag_type = fmt.parseUnsigned(tag_type, str, 16) catch {
            continue;
        };
        break :a std.meta.intToEnum(T, int) catch |err| switch (err) {
            error.InvalidEnumTag => continue,
        };
    };
}
pub fn ui_clear() void {
    debug.print("\x1bc", .{});
    return;
}
pub fn get_ui_textByVim(allocator: mem.Allocator, question: []const u8, input: fs.File.Reader) ![:0]u8 { // <><>
    debug.print("{s} (按下回车开始)\n", .{question});
    input.skipUntilDelimiterOrEof('\n') catch @panic("nb");
    ui_clear();
    const absolute_path: [:0]const u8 = fs.cache_file.create_init(allocator) catch @panic("nb"); // <><>
    defer fs.cache_file.create_deinit(allocator, absolute_path);
    const cmd_vim: [:0]const u8 = fmt.allocPrintZ(allocator, "vim \"{s}\"", .{absolute_path}) catch @panic("nb"); // <><>
    defer allocator.free(cmd_vim);
    debug.assert(c.system(cmd_vim.ptr) != -1);
    const file: fs.File = fs.openFileAbsolute(absolute_path, .{}) catch @panic("nb"); // <><>
    defer file.close();
    const info: [:0]u8 = blk: { // <+><+>
        var blk = file.readToEndAlloc(allocator, math.maxInt(u16)) catch @panic("nb"); // <+><+>
        errdefer allocator.free(blk);
        blk = allocator.realloc(blk, blk.len + 1) catch @panic("nb");
        blk[blk.len - 1] = 0;
        break :blk blk[0 .. blk.len - 1 :0];
    };
    errdefer allocator.free(info);
    return info;
}
